#ifndef LINKEDLIST_H
#define LINKEDLIST_H


template<class T>
class DLList
{
public:
    class node;     // Basic node which is never used but almost every object used in DLL inherits from it.
    class innerNode;// It stores data and its located between head and tail of DLL.
    class tail;     // End of DLList.
    class head;     // Beginning of DLLlist.
    typedef DLList<T>::innerNode INref;

    DLList(){myHead = new head; objectCount=0;};
    ~DLList(){delete myHead;};
//  TYPE 1     IDENTIFIER  ARGUMENTS     DEFINITION
    innerNode& operator[]  (int order);                            // Overriden operator for numerical access.
    void       push_back   (T& data);
    void       push_front  (T& data);
    void       pop_back    ();
    void       pop_front   ();
    void       setOrder    ()            {myHead->setOrder(0);}; // Sets order of all nodes every time it creates one.
    head*      myHead;
    int        objectCount;
};

template<class T>
void DLList<T>::pop_front()     // Removes first member of DLList.
{
    if(objectCount>0)
    {
        myHead->pop_front();
        objectCount--;
        setOrder();
    }
}

template<class T>
void DLList<T>::push_front(T& data)     // Adds member at the beginning of DLList.
{
    myHead->push_front(data);
    setOrder();
    objectCount++;
}

template<class T>
void DLList<T>::pop_back()      // Removes last member of DLList.
{
    if(objectCount>0)
    {
        myHead->pop_back();
        objectCount--;
        setOrder();
    }
}

template<class T>
void DLList<T>::push_back(T& data)      // Adds member at the end of DLList.
{
    myHead->push_back(data);
    setOrder();
    objectCount++;
}


template<class T>
class DLList<T>::node
{
    public:
//  TYPE 1  TYPE 2 IDENTIFIER  ARGUMENTS     DEFINITION
    virtual void   push_back   (T&)=0;
    virtual void   push_front  (T& data)=0;
    virtual void   pop_back    ()=0;
    virtual void   pop_front   ()=0;
    virtual void   setMN    (node* J)=0;
    virtual void   setMP    (node* J)=0;
    virtual void   setOrder    (int x)=0;
    virtual void   test        ()=0;         // Two functions for testing purpose.
    virtual int    test2       ()            {return 9000;};
            node*  myNext;
            node*  myPrevious;

    virtual typename DLList<T>::INref& compare (int x)=0;
};


template<class T>
class DLList<T>::innerNode: public node
{
    public:
    innerNode(node* MP,node* MN,T& data);
    innerNode(): myNext(0),myPrevious(0),myOrder(11){};
    ~innerNode(){delete myNext;delete myPrevious;};
//  TYPE 1   TYPE 2  IDENTIFIER  ARGUMENTS DEFINITION
             int     getMyOrder  ()        {return myOrder;};       // Return order of the node in DLList.
    virtual  void    push_back   (T& data) {myNext->push_back(data);};
    virtual  void    push_front  (T& data) {printf("Error in DLL::push_front.");};     // This function should never be called.
    virtual  void    pop_back    ();
    virtual  void    pop_front   ();
    virtual  void    setMN    (node* J) {myNext=J;};
    virtual  void    setMP    (node* J) {myPrevious=J;};
    virtual  void    setOrder    (int x);
    virtual  void    test        ()        {printf("test: %d", myOrder);}; // Also for testing.
    virtual  int     test2       ()        {return myOrder;};                       //
             T       myT;

    private:
    int myOrder;
    node* myNext;
    node* myPrevious;
    virtual typename DLList<T>::INref& compare(int x);      // Compare function which is called by operator[], it returns
};                                                          // reference to node if its order equals the argument else
                                                            // it continues to next node. It should never reach the tail.


template<class T>
class DLList<T>::tail: public node
{
    public:
    tail(node* mp){myPrevious = mp;}; // When tail is created it instantly sets head as its previous object.
    ~tail(){delete myPrevious;};
//  TYPE 1  TYPE 2 IDENTIFIER  ARGUMENTS  DEFINITION
    virtual void   push_back   (T& data);
    virtual void   push_front  (T& data)  {printf("Error in DLL::push_front.");};   // This functions should never be called.
    virtual void   pop_back    ()         {printf("Error in DLL::pop_back.");};     //
    virtual void   pop_front   ()         {printf("Error in DLL::pop_front.");};    //
    virtual void   setMP    (node* J)  {myPrevious=J;};
    virtual void   setMN    (node* J)  {printf("This function shouldnt be used");}; // String speaks for itself.
    virtual void   setOrder    (int x)    {fout<<"\n";}; // When function setOrder gets to the tail it does nothing except logging info formating.
    virtual void   test        ()         {printf("the tail");};   // Also for testing.
    virtual int    test2       ()         {return 99;};                     //

    private:
    node* myPrevious;
    virtual typename DLList<T>::INref& compare(int x);
};

template<class T>
class DLList<T>::head: public node
    {
    public:
        head(){myNext = new tail(this);};   // Creates tail (end) of DLL and sets it so it goes righ after the head.
        ~head(){delete myNext;};
    //  TYPE 1  TYPE 2 IDENTIFIER  ARGUMENTS  DEFINITION
        virtual void   setMN    (node* J)  {myNext=J;};
        virtual void   setMP    (node* J)  {printf("this function shouldnt be used\n");}; // String speaks for it self.
        virtual void   push_back   (T& data)  {myNext->push_back(data);};
        virtual void   push_front  (T& data);
        virtual void   pop_back    ()         {myNext->pop_back();};
        virtual void   pop_front   ();
        virtual void   setOrder    (int x)    {myNext->setOrder(x);};
        virtual void   test        ()         {printf("the head");}; // TESTIN'
        virtual int    test2       ()         {return 66;};                   //

        virtual typename DLList<T>::INref& compare(int x){return myNext->compare(x);};

        private:
        node* myNext;
    };

template<class T>
void DLList<T>::innerNode::pop_front()
{
    myPrevious->setMN(myNext);
    myNext->setMP(myPrevious);
}

template<class T>
void DLList<T>::innerNode::pop_back()
{
    if(myNext->test2()==99)
    {
        myPrevious->setMN(myNext);
        myNext->setMP(myPrevious);
    }
    else myNext->pop_back();
}

template<class T>
void DLList<T>::innerNode::setOrder(int x)
{
    myOrder = x;
    fout<< " MO:"<<test2()<<" MP:"<<myPrevious->test2() // Info about nodes (myOrder,myPrevious,myNext).
        <<" MN:"<<myNext->test2()<<"\n";                //
    myNext->setOrder(++x);
}


template<class T>
DLList<T>::innerNode::innerNode(node* MP,node* MN,T& data) // When inner node is created it recieves objects to which it should refer
{                                                          // as its next and previous nodes so it knows its place in DLL and it also
    myPrevious=MP;                                         // recieves data which it should store.
    myNext=MN;
    myOrder=85;                                            // For testin'
    myT=data;
    myPrevious->setMN(this);                            // Makes sure that previous object now points on this newly created one.
    myNext->setMP(this);                                //
}

template<class T>
typename DLList<T>::INref& DLList<T>::tail::compare(int x) // Tail should never be reached with compare function.
{
    printf("This member of double linked list doesnt exist, dummy object returned.");
    innerNode* dummy = new innerNode;
    return *dummy;
}

template<class T>
void DLList<T>::tail::push_back(T& data)
{
    myPrevious = new DLList<T>::innerNode(myPrevious,this,data);
}

template<class T>
void DLList<T>::head::pop_front()
{
    if(myNext->test2()!=99)
    myNext->pop_front();
}

template<class T>
void DLList<T>::head::push_front(T& data)
{
        myNext = new DLList<T>::innerNode(this,myNext,data);
}


template<class T>
typename DLList<T>::innerNode& DLList<T>::operator[](int reqOrder)
{
    return myHead->compare(reqOrder);
}

template<class T>
typename DLList<T>::INref& DLList<T>::innerNode::compare(int x)
{
    if(x==myOrder)
        return *this;
    else if(x>myOrder)
    myNext->compare(x);
    else
    printf("Error in comparing of members in double linked list.");
}


#endif // LINKEDLIST_H
